﻿using CNative.Cloud.CPlatform.Diagnostics;
using CNative.Cloud.CPlatform.Messages;
using CNative.Cloud.CPlatform.Serialization;
using CNative.Cloud.CPlatform.Utilities;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using CNativeEvents = CNative.Cloud.CPlatform.Diagnostics.DiagnosticListenerExtensions;

namespace CNative.Cloud.Protocol.Mqtt.Diagnostics
{
     public class MqttTransportDiagnosticProcessor: CPlatform.Diagnostics.Implementation.DefaultTransportDiagnosticProcessor
    {
        private readonly IEntrySegmentContextAccessor _segmentContextAccessor;

        public MqttTransportDiagnosticProcessor(ITracingContext tracingContext, ISerializer<string> serializer, IEntrySegmentContextAccessor contextAccessor)
            : base(tracingContext, serializer)
        {
            transportType = TransportType.Mqtt;
            _segmentContextAccessor = contextAccessor;
        }

        [DiagnosticName(CNativeEvents.CNativeBeforeTransport, TransportType.Mqtt)]
        public override void TransportBefore([Object] TransportEventData eventData)
        {
            var message = eventData.Message.GetContent<RemoteInvokeMessage>();
            var operationName = TransportOperationNameResolver(eventData);
            var context = _tracingContext.CreateEntrySegmentContext(operationName, new MqttTransportCarrierHeaderCollection(eventData.Headers));
            if (!string.IsNullOrEmpty(eventData.TraceId))
                context.TraceId = ConvertUniqueId(eventData).ToString();
            context.Span.AddLog(LogEvent.Message($"Worker running at: {DateTime.Now}"));
            context.Span.SpanLayer = SpanLayer.RPC_FRAMEWORK;
            context.Span.AddTag(Tags.MQTT_CLIENT_ID, eventData.TraceId.ToString());
            context.Span.AddTag(Tags.MQTT_METHOD, eventData.Method.ToString());
            context.Span.AddTag(Tags.MQTT_PARAMETERS, _serializer.Serialize(message.Parameters));
            context.Span.AddTag(Tags.MQTT_BROKER_ADDRESS, NetUtils.GetHostAddress().ToString());
        }

        [DiagnosticName(CNativeEvents.CNativeAfterTransport, TransportType.Mqtt)]
        public override void TransportAfter([Object] ReceiveEventData eventData)
        {
            var context = _segmentContextAccessor.Context;
            if (context != null)
            {
                if (eventData != null && eventData.Message != null && eventData.Message.Content != null)
                    context.Span.AddLog(LogEvent.Reponse(_serializer.Serialize(eventData.Message.Content)));
                _tracingContext.Release(context);
            }
        }

        [DiagnosticName(CNativeEvents.CNativeErrorTransport, TransportType.Mqtt)]
        public override void TransportError([Object] TransportErrorEventData eventData)
        {
            var context = _segmentContextAccessor.Context;
            if (context != null)
            {
                context.Span.ErrorOccurred(eventData.Exception);
                _tracingContext.Release(context);
            }
        }

        public override UniqueId ConvertUniqueId(TransportEventData eventData)
        {
            long part1 = 0, part2 = 0, part3 = 0;
            UniqueId uniqueId = new UniqueId();
            var bytes = Encoding.Default.GetBytes($"{eventData.TraceId}-{nameof(MqttTransportDiagnosticProcessor)}");
            part1 = BitConverter.ToInt64(bytes, 0);
            if (eventData.TraceId.Length > 8)
                part2 = BitConverter.ToInt64(bytes, 8);
            if (eventData.TraceId.Length > 16)
                part3 = BitConverter.ToInt64(bytes, 16);
            if (!string.IsNullOrEmpty(eventData.TraceId))
                uniqueId = new UniqueId(part1, part2, part3);
            return uniqueId;
        }
    }
}